import { ColumnDef } from '@tanstack/react-table';
import React from 'react';
import { editWidgetConfigs, getWidgetDefaultValue } from '../../TableFilter';
import {
  deepClone,
  isArray,
  isBoolean,
  isNullOrUndefined,
  isString,
} from '../../utils';
import {
  TABLE_ACTION_CELL,
  TABLE_INDEX,
  TABLE_SELECT_CELL,
  defaultRowKey,
} from '../config';
import type {
  AnyObject,
  Column,
  ColumnDataKeyInfo,
  ColumnFormulaInfo,
  ColumnMeta,
  CurOperateInfo,
  DragSortType,
  DragType,
  EditType,
  Errors,
  FilterCopyKey,
  RowSelection,
  Rule,
  Rules,
  TableContextRef,
  TableModalParams,
  TablePagination,
  TableSelectType,
  ValidateError,
} from '../type';
import validate from '../utils/json-schema';
import calculate from './calculate';
import { getAllFlatColumns, getColumn, getColumnRule } from './column';
import type { Schema } from './json-schema/interface';

type RowData<T extends AnyObject> = {
  [key in keyof T]?: any;
};
// 生成默认行数据
export function createInitRowData<T extends AnyObject>(
  columns: Column<T>[],
  filterColumns: string[] = [TABLE_INDEX, TABLE_ACTION_CELL, TABLE_SELECT_CELL],
) {
  const config: {
    [key in EditType]?: any;
  } = {
    number: 0,
    string: '',
  };
  const tableColumns = getAllFlatColumns(columns);
  const result: RowData<T> = {};
  tableColumns.forEach((column) => {
    if (!filterColumns.includes(column.id)) {
      const columnMeta: ColumnMeta<T> = column.meta || {};
      const { dataKey } = columnMeta;
      const columnId = dataKey || column.id;
      const meta =
        ((dataKey && getColumn(dataKey, tableColumns)) || column).meta || {};
      const { editType, dataType } = meta;
      const type = editType || dataType || 'string';

      result[columnId as keyof T] = !isNullOrUndefined(config[type])
        ? config[type]
        : '';
    }
  });
  return result;
}
export function createInitTableData<T extends AnyObject>(
  dataSource: T[],
  columns: Column<T>[],
  defaultRows = 2,
) {
  if (!dataSource || !dataSource.length) {
    const tableData = [];
    if (columns.length) {
      for (let i = 0; i < defaultRows; i++) {
        tableData.push(createInitRowData(columns));
      }
    }
    return tableData;
  }
  return dataSource;
}
// 添加、复制
export function getAddHandler<T extends AnyObject>(
  columns: Column<T>[],
  onChangeDataSource?: (dataSource: T[]) => void,
) {
  return (
    rowIndex: number,
    dataSource: T[],
    record?: T | T[],
    filterCopyKey: FilterCopyKey = [],
  ) => {
    const tableData: T[] = deepClone(dataSource);

    const addData: T[] = [];
    if (!record) {
      addData.push(createInitRowData(columns) as T);
    } else {
      // 批量添加或复制
      const dataList = isArray(record) ? record : [record];
      const filterKeys = isString(filterCopyKey)
        ? [filterCopyKey]
        : filterCopyKey;

      if (filterKeys && filterKeys.length) {
        for (let item of dataList) {
          const data: any = {};
          for (let key in item) {
            if (!filterKeys.includes(key)) data[key] = item[key];
          }
          addData.push(data);
        }
      } else {
        addData.push(...dataList);
      }
    }
    tableData.splice(rowIndex + 1, 0, ...addData);
    onChangeDataSource(tableData);
    return tableData;
  };
}
// 更新
export function getUpdateHandler<T extends AnyObject>(
  onChangeDataSource?: (dataSource: T[]) => void,
) {
  return (rowIndex: number, record: T, dataSource: T[]) => {
    const tableData: T[] = deepClone(dataSource);

    tableData[rowIndex] = Object.assign(tableData[rowIndex], record);
    onChangeDataSource(tableData);
    return tableData;
  };
}
// 删除
export function getDeleteHandler<T extends AnyObject>(
  onChangeDataSource?: (dataSource: T[]) => void,
) {
  return (rowIndex: number, dataSource: T[]) => {
    const tableData: T[] = deepClone(dataSource);
    if (tableData.length > 1) {
      tableData.splice(rowIndex, 1);
      onChangeDataSource && onChangeDataSource(tableData);
    }
    return tableData;
  };
}

/**
 * 处理表格数据 key 字段
 *
 * @param isRequiredKey 是否必须添加key 字段，item[rowKey]空时不加key
 */
export function handleRowKey<T extends AnyObject>(
  dataSource: T[] = [],
  pagination?: TablePagination,
  rowKey: string = defaultRowKey,
  isRequiredKey?: boolean,
) {
  const deep = (dataSource: T[] = [], parentKey?: string) => {
    return dataSource.map((item, i) => {
      const result = { ...item };
      let key = item[rowKey];

      if (isNullOrUndefined(key) && isRequiredKey) {
        if (pagination && pagination.current) {
          key = (pagination.current - 1) * pagination.pageSize + i + 1;
        } else {
          key = i + 1;
        }
      }
      if (parentKey) key = `${parentKey}.${key}`;
      if (result.subRows && result.subRows.length) {
        // @ts-ignore
        result.subRows = deep(result.subRows, key);
      }
      if (!isNullOrUndefined(key)) result['key' as keyof T] = key;
      return result;
    });
  };
  return deep(dataSource);
}
export function handleDataKey<T extends AnyObject>(
  dataSource: T[],
  dataKeyInfo: ColumnDataKeyInfo<T>,
) {
  return dataSource.map((item) => {
    const res = { ...item };
    for (let key in dataKeyInfo) {
      if (
        isNullOrUndefined(res[key]) &&
        !isNullOrUndefined(res[dataKeyInfo[key]])
      ) {
        res[key as keyof T] = res[dataKeyInfo[key]];
      }
    }
    return res;
  });
}

//计算公式值
export function calculateTableFormula<T extends AnyObject>(
  dataSource: T[],
  formula: ColumnFormulaInfo<T>,
) {
  return dataSource.map((data) => {
    const result = { ...data };
    for (let columnId in formula) {
      const valueFormula = formula[columnId].replace(
        /\{(\w+)\}/g,
        (_, columnId) => {
          return data[columnId];
        },
      );
      const value = calculate(valueFormula);
      // @ts-ignore
      result[columnId] = value;
    }
    return result;
  });
}
// 生成表格校验规则
export function createTableSchema<T extends AnyObject>(rules: Rules<T>) {
  const schema: Schema = { required: [], properties: {} };
  const getCheckRuleKeys = (ruleKeys: { [key in keyof Rule]?: any }) => {
    const result: (keyof Rule)[] = [];
    for (let key in ruleKeys) {
      if (!isNullOrUndefined(ruleKeys[key])) result.push(key as keyof Rule);
    }
    return result;
  };
  const createMessages = (key: string | string[], message: string) => {
    const result: { [key in string]?: string } = {};
    const keys = isArray(key) ? key : [key];
    return keys.reduce((result, key) => {
      result[key] = message;
      return result;
    }, result);
  };

  for (let id in rules) {
    const columnRules = rules[id];
    for (let rule of columnRules) {
      const { required, type, min, max, pattern, message } = rule;
      if (required) {
        schema.required.push(message ? { key: id, message } : id);
      }
      const { properties } = schema;
      if (!properties[id]) properties[id] = {};
      type && (properties[id].type = type);
      pattern && (properties[id].pattern = pattern);

      // number, string, array 最值配置
      const configs = {
        number: { min: 'minNum', max: 'maxNum' },
        string: { min: 'minLength', max: 'maxLength' },
        array: { min: 'minLength', max: 'maxLength' },
      };
      const config = configs[type];
      if (config) {
        !isNullOrUndefined(min) && (properties[id][config.min] = min);
        !isNullOrUndefined(max) && (properties[id][config.max] = max);
      }
      const rulesKeys = getCheckRuleKeys(
        config
          ? { type, [config.min]: min, [config.max]: max, pattern }
          : { type, pattern },
      );
      properties[id].messages = createMessages(rulesKeys, message);
    }
  }
  return schema;
}
export function createTableModalParams<T extends AnyObject>(
  columns: ColumnDef<T>[],
  curOperateInfo: CurOperateInfo<T>,
) {
  const { data, meta, curValue } = curOperateInfo;
  const columnId = meta.dataKey || curOperateInfo.columnId;
  const listParams: TableModalParams<T> = {};
  columns.forEach((column) => {
    const columnMeta = (column.meta as ColumnMeta<T>) || {};
    const { dataKey, dataType, editType } = columnMeta;
    const key: keyof T = dataKey || column.id;
    const type = editType || dataType;
    const widgetType = editWidgetConfigs[type];
    const defaultValue = getWidgetDefaultValue(widgetType);
    const dataValue = !isNullOrUndefined(curValue)
      ? curValue
      : data[key as string];
    const value = columnId === key ? dataValue : defaultValue;
    listParams[key] = value;
  });
  return listParams;
}

export function createTableId(prefix?: string, len = 12) {
  const getRandom = (s: number, e: number) =>
    Math.floor(Math.random() * (e - s + 1)) + s;
  let id = prefix ? `${prefix}-` : '';
  for (let i = 0; i < len; i++) {
    id += String.fromCharCode(getRandom(65, 90));
  }
  return id;
}

export function getDragTypes(dragSrotType: DragSortType) {
  if (dragSrotType) {
    const alldragType: DragType[] = ['row', 'column'];
    return isBoolean(dragSrotType)
      ? alldragType
      : isArray(dragSrotType)
      ? dragSrotType
      : [dragSrotType];
  }
  return [];
}

export function getTableRef(
  Table: <T extends AnyObject, P>(
    props: P,
    ref?: React.ForwardedRef<TableContextRef<T>>,
  ) => JSX.Element,
) {
  return React.forwardRef(Table) as <T, P>(
    props: P & { ref?: React.ForwardedRef<TableContextRef<T>> },
  ) => ReturnType<typeof Table>;
}

export function createInitRuleRowData<T extends AnyObject>(
  tableColumns: Column<T>[],
  ruleKeys: string[],
) {
  const config: {
    [key in EditType]?: any;
  } = {
    number: 0,
    string: '',
  };
  const result: RowData<T> = {};
  const columns = getAllFlatColumns(tableColumns).filter((column) =>
    ruleKeys.includes(column.id),
  );
  columns.forEach((column) => {
    const meta = (column && column.meta) || {};
    const { editType, dataType } = meta;
    const type = editType || dataType || 'string';

    result[column.id as keyof T] = !isNullOrUndefined(config[type])
      ? config[type]
      : '';
  });
  return result;
}
/** 检查原始数据与对比数据是否相同 feat: 检查数组与对象值 */
function checkIsEquality(original: AnyObject, comparative: AnyObject) {
  const originalKeys = Object.keys(original);
  const comparativeKeys = Object.keys(comparative);
  if (originalKeys.length !== comparativeKeys.length) return false;
  for (let originalKey of originalKeys) {
    if (
      isNullOrUndefined(comparative[originalKey]) ||
      original[originalKey] !== comparative[originalKey]
    )
      return false;
  }
  return true;
}
// 校验整个表格，不校验与默认行数据一致的数据
export function validateTable<T extends AnyObject>(
  tableColumns: Column<T>[],
  dataSource: any[],
) {
  const errors: Errors<T> = [];
  const tableRules = getColumnRule(tableColumns);
  const ruleKeys = Object.keys(tableRules);
  if (ruleKeys && ruleKeys.length) {
    const schema = createTableSchema(tableRules);
    const initRowData = createInitRuleRowData(tableColumns, ruleKeys);
    const tableValidationData = dataSource.map((item) => {
      const result: any = {};
      ruleKeys.forEach((key) => {
        if (isNullOrUndefined(item[key])) {
          const column = getColumn(key, tableColumns);
          const meta = (column && column.meta) || {};
          const dataKey = meta.dataKey || key;
          result[key] = item[dataKey];
        } else {
          result[key] = item[key];
        }
      });
      return result;
    });

    tableValidationData.forEach((data, i) => {
      if (!checkIsEquality(data, initRowData)) {
        const validatedErrors = validate(data, schema);
        if (validatedErrors && validatedErrors.length) {
          const updateError: ValidateError<T> = {};
          validatedErrors.forEach((error) => {
            const { key } = error;
            if (!updateError[key]) updateError[key as keyof T] = [];
            updateError[key].push(error);
          });
          errors[i] = updateError;
        }
      }
    });
  }
  return errors;
}

// 选择表格方法
export function getSelectedRows<T extends AnyObject>(
  selectedRows: T[],
  hadSelectedRows: T[],
  isCanRepeatSelection = true,
) {
  if (isCanRepeatSelection) {
    return selectedRows;
  } else {
    const hadSelectedKeys = hadSelectedRows.map((item) => item.key);
    return selectedRows.filter((item) => !hadSelectedKeys.includes(item.key));
  }
}
export function getIsAllRowsSelected<T extends AnyObject>(
  dataSource: T[],
  selectedRows: T[],
) {
  const selectedRowKeys = selectedRows.map((item) => item.key);
  return (
    dataSource.length &&
    selectedRowKeys.length &&
    dataSource.every((item) => selectedRowKeys.includes(item.key))
  );
}
export function getIsSelected<T extends AnyObject>(
  key: string,
  selectedRows: T[],
) {
  return selectedRows.some((item) => item.key === key);
}

export function addSelectedRow<T extends AnyObject>(
  data: T | T[],
  selectedRows: T[],
) {
  const selectedRowsData: T[] = deepClone(selectedRows);
  const rows = isArray(data)
    ? data.filter((item) => !getIsSelected(item.key, selectedRows))
    : [data];
  const selectedData = selectedRowsData.concat(rows);
  return selectedData;
}
export function removeSelectedRow<T extends AnyObject>(
  data: T | T[],
  selectedRows: T[],
) {
  const removeDataKeys = (isArray(data) ? data : [data]).map(
    (item) => item.key,
  );
  const rows: T[] = [];
  selectedRows.forEach((item) => {
    if (!removeDataKeys.includes(item.key)) rows.push(deepClone(item));
  });
  return rows;
}
export function selectRow<T extends AnyObject>(
  data: T,
  selectedRows: T[],
  selectType: TableSelectType,
): T[] {
  if (selectType === 'checkbox') {
    return getIsSelected(data.key, selectedRows)
      ? removeSelectedRow(data, selectedRows)
      : addSelectedRow(data, selectedRows);
  } else {
    return addSelectedRow(data, []);
  }
}
export function selectAllRow<T extends AnyObject>(
  data: T[],
  selectedRows: T[],
) {
  return getIsAllRowsSelected(data, selectedRows)
    ? removeSelectedRow(data, selectedRows)
    : addSelectedRow(data, selectedRows);
}

export const createRowSelection = <T extends AnyObject>(
  rowKey: string,
  rowSelection: RowSelection<T>,
) => {
  if (rowSelection) {
    const { isCanRepeatSelection = true, selectedRows = [] } = rowSelection;
    const selectCellProps = !isCanRepeatSelection
      ? (data: T, index: number) => ({
          ...rowSelection.selectCellProps?.(data, index),
          disabled: selectedRows
            .map((item) => item[rowKey])
            .includes(data[rowKey]),
        })
      : rowSelection.selectCellProps;
    return { ...rowSelection, selectCellProps };
  }

  return rowSelection;
};
export const setSelectionDataSource = <T extends AnyObject>(
  dataSource: T[],
  rowSelection: RowSelection<T>,
) => {
  if (
    rowSelection &&
    rowSelection.selectCellProps &&
    !rowSelection.isCanRepeatSelection
  ) {
    return dataSource.filter(
      (data, index) => !rowSelection.selectCellProps(data, index).disabled,
    );
  }
  return dataSource;
};
